#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2023/7/7 13:22
# @Author : 搬砖客
# @File : assert_util.py
# @Software: PyCharm
import json
import traceback
import jsonpath
from utils.logging_utils.log_util import write_log, write_error_log, write_warning_log
from utils.mysql_utils.database_util import Database
from utils.requests_utils.replace_load import replace_load


class AssertUtil:
    def __init__(self):
        pass

    # 进行响应内容断言的封装
    @staticmethod
    def validate_result(expect_result, real_result, status_code, real_time, assert_time):
        """

        :param expect_result: 预期结果
        :param real_result: 实际返回的内容
        :param status_code: 实际返回的状态码
        :param real_time: 实际响应时间
        :param assert_time: 实际响应断言是否启用
        :return: 返回flag，0代表成功，其他代表失败
        """
        try:
            # 收集日志
            write_log("开始进行接口断言")
            write_log("接口的预期结果：{}".format(replace_load(str(expect_result))))
            write_log("接口的实际结果：{}".format(str(json.dumps(real_result, ensure_ascii=False)).replace("None", "null")))
            # 断言是否成功的标记，0代表成功，其他代表失败
            flag = 0
            if expect_result and isinstance(expect_result, list):
                for expect_value in expect_result:
                    for key, value in dict(expect_value).items():
                        value = replace_load(value)
                        '''
                        判断断言方式: 
                        [{'equals': {'status_code': 200}},
                        {'equals': {'message': 'success'}},
                        {'equals': {'code': 1}}]

                        '''
                        if key == "equals":
                            for assert_key, assert_value in dict(value).items():
                                if assert_key == "status_code":
                                    write_log("进行响应code断言")
                                    if status_code != assert_value:
                                        flag = flag + 1
                                        write_error_log(
                                            "接口响应内容的status_code断言失败，预期结果为：{}，实际结果为：{}".format(assert_value,
                                                                                              status_code))
                                else:
                                    write_log("该测试用例使用了字符串的equals断言")
                                    key_list = jsonpath.jsonpath(real_result, "$..%s" % assert_key)
                                    if key_list:
                                        if assert_value not in key_list:
                                            flag = flag + 1
                                            write_error_log(
                                                "接口响应内容断言失败，断言类型‘equals’，预期值 ’{}‘ 不在 ’{}‘里".format(assert_value,
                                                                                                   key_list))
                                    else:
                                        flag = flag + 1
                                        write_error_log("接口响应内容断言失败，返回结果中不存在key：{}".format(assert_key))

                        elif key == "contains":
                            write_log("该测试用例使用了字符串的contains断言")

                            real_result_str = str(json.dumps(real_result, ensure_ascii=False)).replace("'",
                                                                                                       '"').replace(" ",
                                                                                                                    "").replace(
                                "None", "null")
                            value = str(value).replace("'", '"').replace(" ", "").replace("None", "null")
                            if value not in real_result_str:
                                flag = flag + 1
                                write_error_log(
                                    "接口响应内容断言失败，断言类型‘contains’，预期结果 ’{}‘不在实际结果 ’{}‘中".format(value, real_result_str))
                        elif key == "time":
                            write_log("该测试用例使用了响应时间断言")
                            if assert_time:
                                if real_time >= value:
                                    flag = flag + 1
                                    write_error_log("接口响应内容断言失败，实际响应时间[{}]ms大于预计响应时间[{}]ms".format(real_time, value))
                            else:
                                write_log("响应时间断言开关未启用")
                        elif key == "jsonpath":
                            real_result_jsonpath = jsonpath.jsonpath(real_result, value['path'])[0]
                            value['value'] = replace_load(value['value'])
                            if value['type'] == "==":
                                write_log("进行jsonpath的equals断言")
                                if str(value['value']) != str(real_result_jsonpath):
                                    flag = flag + 1
                                    write_error_log(
                                        "接口响应内容断言失败，断言类型‘jsonpath’的equals，预期值 ’{}‘ 不在 ’{}‘ 里".format(value['value'],
                                                                                                     real_result_jsonpath))
                            elif value['type'] == "in" or "IN":
                                write_log("该测试用例使用了jsonpath的contains断言")
                                if str(value['value']) not in str(real_result_jsonpath):
                                    flag = flag + 1
                                    write_error_log(
                                        "接口响应内容断言失败，断言类型‘jsonpath’的contains，预期值 ’{}‘ 不在 ’{}‘里".format(value['value'],
                                                                                                      real_result_jsonpath))

                        else:
                            write_warning_log("框架不支持该断言方式：{}".format(key))

            if flag == 0:
                write_log("接口请求成功")
                write_log("--------------------接口请求结束---------------------\n")

            else:
                write_log("--------------------接口请求结束---------------------\n")
                raise AssertionError("接口返回内容断言失败")

        except Exception as e:
            write_log("接口请求失败")
            write_log("--------------------接口请求结束---------------------\n")
            write_error_log("接口响应内容断言异常，异常信息为：%s" % str(traceback.format_exc()))

    # 数据库断言封装
    @staticmethod
    def validate_sql_result(expect_sql_result, state="all"):
        """
        思路： 如果yaml文件有sql一级关键字，则进行数据库查询断言,
        :param expect_sql_result: 预期结果
        :param state: 设置查询全部还是一条，all或one
        :return:
        """
        try:
            flag = 0
            switch_state = ""
            sql_statement = ""
            state_value = state
            expect_result = None
            actual_result = None
            sql_assert_type = None
            # 提取sql语句和state的值，没有state这个key，则默认state_value默认"all"
            try:
                for sql_dict in expect_sql_result:
                    for sql_key, sql_value in dict(sql_dict).items():
                        if sql_key == "sql_statement":
                            sql_statement = str(replace_load(sql_value))
                        elif sql_key == "state":
                            state_value = sql_value

            except:
                pass

            if expect_sql_result and isinstance(expect_sql_result, list):
                for expect_value in expect_sql_result:
                    for key, value in dict(expect_value).items():
                        # todo 增加不进行断言的日志输出
                        if key == "switch":
                            if value is True:
                                switch_state = True
                                write_log("开始进行数据库断言")
                            else:
                                switch_state = False
                        if key == "sql_assert" and switch_state:
                            value = str(replace_load(value))
                            real_result = Database().query_sql(sql_statement, state=state_value)
                            write_log("数据库的预期结果为：{}".format(value))
                            write_log("数据库的实际结果为：{}".format(real_result))
                            if value not in real_result:
                                flag = flag + 1
                                expect_result = value
                                actual_result = real_result
                        elif key == "sql_reserve" and switch_state:
                            sql_assert_type = "reserve"
                            value = str(replace_load(value))
                            real_result_reserve = Database().query_sql(sql_statement, state=state_value)
                            write_log("数据库的预期不会存在的结果内容：{}".format(value))
                            write_log("数据库的实际结果为：{}".format(real_result_reserve))
                            if value in real_result_reserve:
                                flag = flag + 1
                                expect_result = value
                                actual_result = real_result_reserve

            if switch_state is True:
                if flag == 0:
                    write_log("数据库断言成功")
                else:
                    if sql_assert_type == "reserve":
                        raise AssertionError("数据库断言失败，预期不会存在的内容’{}‘，实际却存在结果’{}‘".format(expect_result, actual_result))
                    else:
                        raise AssertionError("数据库断言失败，预期结果为：’{}‘，实际结果为：’{}‘".format(expect_result, actual_result))

            else:
                write_log("该测试用例不进行数据库断言")

        except Exception as e:
            raise AssertionError("数据库断言异常，异常信息为：{}".format(e))
